          
#include <assert.h>
#include <stdio.h>
#include <sstream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "svr_reactor_facade.h"
#include "tcp_handle_base.h"
#include "alloc.h"
#include "log_mgr.h"

//////////////////////////////////
// class CTcpHandleBase
CTcpHandleBase::CTcpHandleBase(uint32_t dwMaxSendBufLen, uint32_t dwMaxRecvBufLen)
: m_pReactor(NULL)
, m_pSink(NULL)
, m_iConnHandle(-1)
, m_ushPeerHostPort(0)
, m_nSendPos(0)
, m_nRecvPos(0)
, m_bNeedOnSend(false)
, m_l_nPacketLen(0)
, m_l_nCurrBufSendSize(0)
, m_l_nCurrBufRecvSize(0)
, m_l_nPos(0)
, m_cStatus(S_CLOSE)
, m_ulUserHandle(0)
, m_dwConnToken(0)
, m_dwActiveTimer(0)
{
	assert(dwMaxSendBufLen > 0);
	assert(dwMaxRecvBufLen > 0);

	m_dwMaxSendBufLen = (dwMaxSendBufLen+PKG_HEAD_LEN)*10;
	m_dwMaxRecvBufLen = (dwMaxRecvBufLen+PKG_HEAD_LEN)*10;

	m_dwSendBufLen = 0;
	m_dwRecvBufLen = 0;	
	m_achSendBuf = NULL;
	m_achRecvBuf = NULL;
}

CTcpHandleBase::~CTcpHandleBase()
{
	if(m_achSendBuf)
		YALLOC->yFree(m_achSendBuf);

	if(m_achRecvBuf)
		YALLOC->yFree(m_achRecvBuf);
}

void CTcpHandleBase::Reset()
{
	m_iConnHandle = -1;
	m_ushPeerHostPort = 0;
	m_nSendPos = 0;
	m_nRecvPos = 0;
	m_bNeedOnSend = false;
	m_l_nPacketLen = 0;
	m_l_nCurrBufSendSize = 0;
	m_l_nCurrBufRecvSize = 0;
	m_l_nPos = 0;
	m_cStatus = S_CLOSE;
	m_ulUserHandle = 0;
	m_dwConnToken = 0;
	m_dwActiveTimer = 0;

	if(m_achSendBuf)
		YALLOC->yFree(m_achSendBuf);

	if(m_achRecvBuf)
		YALLOC->yFree(m_achRecvBuf);
	
	m_dwSendBufLen = 0;
	m_dwRecvBufLen = 0;	
	m_achSendBuf = NULL;
	m_achRecvBuf = NULL;
		
}

ITCPNetHandler* CTcpHandleBase::Clone() const
{
    CTcpHandleBase* pObj = new CTcpHandleBase(m_dwMaxSendBufLen/10, m_dwMaxRecvBufLen/10);
    pObj->m_pReactor = this->m_pReactor;
    pObj->m_pSink = this->m_pSink;
	
	return pObj;
}

int CTcpHandleBase::OnConnect(
                              int iConnHandle,
                              unsigned int uiPeerHost,
                              unsigned short ushPeerPort,
                              int iFlag)
{
    m_iConnHandle = iConnHandle;

	struct in_addr addr;
	addr.s_addr = uiPeerHost;
    m_sPeerHostIP = inet_ntoa(addr);
	m_ushPeerHostPort = ushPeerPort;

	assert(m_cStatus != S_CONNECTED);
	//success
	if(iFlag == 0)
	    m_cStatus = S_CONNECTED;

    assert(m_pSink);
    m_pSink->OnConnect(this, m_sPeerHostIP.c_str(), ushPeerPort);
    
    return 0;
}

int CTcpHandleBase::OnHalfConnect(
                              int iConnHandle,
                              unsigned int uiPeerHost,
                              unsigned short ushPeerPort,
                              int iFlag)
{
    m_iConnHandle = iConnHandle;

	struct in_addr addr;
	addr.s_addr = uiPeerHost;
    m_sPeerHostIP = inet_ntoa(addr);
	m_ushPeerHostPort = ushPeerPort;

	assert(m_cStatus == S_CLOSE);
	//success
	m_cStatus = S_HALF_CONN;
    
    return 0;
}

int CTcpHandleBase::OnRecv(
                           int iConnHandle,
                           char* pRecvBuf,
                           uint32_t nBufLen,
                           int iFlag)
{
    assert(m_iConnHandle == iConnHandle);

    m_l_nCurrBufRecvSize = m_nRecvPos + nBufLen;

	//check recv buff size
	if(m_dwMaxRecvBufLen < m_l_nCurrBufRecvSize)
	{
		ERROR("OnRecvError, Recv Buff Reach Limit, RecvPos[%u], BufLen[%u], CurrBufRecvSize[%u] > MaxRecvBufLen[%u]", 
				m_nRecvPos, nBufLen, m_l_nCurrBufRecvSize, m_dwMaxRecvBufLen);
		return ERR_RECV_BUFF_FULL;
	}

	if(m_dwRecvBufLen < m_l_nCurrBufRecvSize)
	{
		m_dwRecvBufLen = (m_l_nCurrBufRecvSize+MEMORY_PIECE-1)&~(MEMORY_PIECE-1); 
		if(m_achRecvBuf == NULL)
			m_achRecvBuf = YALLOC->yMalloc(m_dwRecvBufLen);
		else
			m_achRecvBuf = YALLOC->yRealloc(m_achRecvBuf, m_dwRecvBufLen);
		if(m_achRecvBuf == NULL)
		{
			ERROR("OnRecvError, yAlloc Memory Fail, Use:%ld, Request:%d", YALLOC->lUse, nBufLen);
			return ERR_ALLOC_MEM;
		}
	}

    // save recv data
    memcpy(m_achRecvBuf+m_nRecvPos, pRecvBuf, nBufLen);

    if(m_l_nCurrBufRecvSize < PKG_HEAD_LEN)
    {
        m_nRecvPos = m_l_nCurrBufRecvSize;
        return 0;
    }

    m_l_nPos = 0;
    m_l_nPacketLen = 0;
	bool hasOnRecv = false;
    
    while(m_l_nCurrBufRecvSize >= PKG_HEAD_LEN)
    {
		if(m_cStatus == S_CLOSE)
			return ERR_CONN_STATUS;
		
		m_l_nPacketLen =  (int)(((int)((uint8_t)m_achRecvBuf[m_l_nPos+0]))
									  +(((int)((uint8_t)m_achRecvBuf[m_l_nPos+1]))<<8)
									  +(((int)((uint8_t)m_achRecvBuf[m_l_nPos+2]))<<16))	;
		//todo check pkg len 
		if(m_l_nPacketLen > MAX_PKG_LEN)
		{
			ERROR("OnRecvError, PkgLen Invalid, PacketLen[%u] > MAX_PKG_LEN[%lu]", 
					m_l_nPacketLen, MAX_PKG_LEN);
			return ERR_PKG_TOO_LARGE;
		}
		
		// Need more data to fill a packet
        if(m_l_nCurrBufRecvSize < (PKG_HEAD_LEN + m_l_nPacketLen))
        {
			if(hasOnRecv)
				m_pSink->OnBreakRecv(this);
            break;
        }
        
        assert(m_pSink);
        m_pSink->OnRecv(this, m_achRecvBuf + m_l_nPos, PKG_HEAD_LEN + m_l_nPacketLen, m_l_nCurrBufRecvSize - PKG_HEAD_LEN - m_l_nPacketLen);

		m_l_nPos += (PKG_HEAD_LEN + m_l_nPacketLen);
		m_l_nCurrBufRecvSize -= (PKG_HEAD_LEN + m_l_nPacketLen);
		hasOnRecv = true;
    }
	
	if(m_cStatus == S_CLOSE)
		return ERR_CONN_STATUS;
    
    if(m_l_nCurrBufRecvSize == 0) // All buffer has been callbacked to upper layer
    {
        m_nRecvPos = 0;
    }
    else
    {
		if(hasOnRecv)
			m_pSink->OnBreakRecv(this);
        memmove(m_achRecvBuf, m_achRecvBuf + m_l_nPos, m_l_nCurrBufRecvSize);
        m_nRecvPos = m_l_nCurrBufRecvSize;
    }
    
    return 0;
}

int CTcpHandleBase::OnSend(
						   int iConnHandle
						   )
{
    assert(m_iConnHandle == iConnHandle);
    
    if(m_bNeedOnSend)
    {
        assert(m_pSink);
        m_pSink->OnSend(this);
        
    }
    
    return 0;
}

int CTcpHandleBase::OnClose(
                            int iConnHandle,
                            int iCloseCode,
                            int iFlag)
{
	if(m_cStatus == S_CLOSE)
		return 0;
    assert(m_iConnHandle == iConnHandle);
    assert(m_pSink);

    m_cStatus = S_CLOSE;
    
    m_pSink->OnClose(this, iCloseCode);
    
    return 0;
}

int CTcpHandleBase::ConnectTCPServer(
                     const char* pszIP,
                     uint16_t ushPort,            
					 ITCPNetHandler*& pOutHandler,
                     bool bAsync)
{
    assert(m_pReactor);
    return m_pReactor->ConnectToServer(
        this,
        ushPort, 
        pszIP,
        pOutHandler,
        bAsync);
}

int CTcpHandleBase::SendData(
                             const char* pSendBuf,
                             uint32_t nBufLen
                             )
{
    if(m_cStatus == S_CLOSE || m_iConnHandle == -1)
    {
        return ERR_CONN_STATUS;   // Not ready
    }

    m_l_nCurrBufSendSize = m_nSendPos + nBufLen;
	
	//check send buff size
    if(m_l_nCurrBufSendSize > m_dwMaxSendBufLen)
    {
    	m_bNeedOnSend = true;
		if(m_nSendPos > 0)
			SendData(); // OnSend Directly
		if(m_l_nCurrBufSendSize > m_dwMaxSendBufLen)
		{
			ERROR("SendDataError, Recv Buff Reach Limit,  SendPos[%u], BufLen[%u], CurrBufSendSize[%u] > MaxSendBufLen[%u]", 
					m_nSendPos, nBufLen, m_l_nCurrBufSendSize, m_dwMaxSendBufLen);
        	return ERR_SEND_BUFF_FULL;   
		}
    }

	if(m_l_nCurrBufSendSize == 0)
	{
        return 0;
	}

	if(m_dwSendBufLen < m_l_nCurrBufSendSize)
	{
		m_dwSendBufLen = (m_l_nCurrBufSendSize+MEMORY_PIECE-1)&~(MEMORY_PIECE-1); 
		if(m_achSendBuf == NULL)
			m_achSendBuf = YALLOC->yMalloc(m_dwSendBufLen);
		else
			m_achSendBuf = YALLOC->yRealloc(m_achSendBuf, m_dwSendBufLen);
		if(m_achSendBuf == NULL)
		{
			ERROR("SendDataError, yAlloc Memory Fail, Use:%ld, Request:%d", YALLOC->lUse, nBufLen);
			return ERR_ALLOC_MEM;
		}
	}

	//save send data to send buff
	if(pSendBuf)
	{
		memcpy(m_achSendBuf + m_nSendPos, pSendBuf, nBufLen);
	}
    
    assert(m_pReactor);
    
    int szByteSent = m_pReactor->Send(
        m_iConnHandle,
        m_achSendBuf, 
        m_l_nCurrBufSendSize);
    
    if(szByteSent > 0)  // Bytes have been sent
    {
        if(szByteSent != m_l_nCurrBufSendSize)
        {
            //printf("NeedOnSend, szByteSent[%d] != m_l_nCurrBufSendSize[%d]\n", szByteSent, m_l_nCurrBufSendSize);
            assert(m_l_nCurrBufSendSize > szByteSent);
            
            memmove(m_achSendBuf, m_achSendBuf + szByteSent, m_l_nCurrBufSendSize - szByteSent);
            m_nSendPos = m_l_nCurrBufSendSize - szByteSent;
            
            m_bNeedOnSend = true;
            m_pReactor->NeedOnSendCheck(m_iConnHandle);
            
            return 0;
        }
        else    // szByteSent == m_l_nCurrBufSendSize
        {
    		m_bNeedOnSend = false;
            m_nSendPos = 0;
            return 0;
        }
    }
   
    // No byte send
    m_nSendPos = m_l_nCurrBufSendSize;
    m_bNeedOnSend = true;
    m_pReactor->NeedOnSendCheck(m_iConnHandle);

    return 0; // No byte send
}

int CTcpHandleBase::Close()
{
    if(m_cStatus == S_CLOSE)
       	return 0;
	
    m_cStatus = S_CLOSE;

    assert(m_pReactor);
    int iRetCode = m_pReactor->Close(m_iConnHandle);
    this->Reset();
    return iRetCode;
}


